#include "dht.h"

#include "tos_k.h"

#define lineDown()      GPIO_ResetBits(sensor->DHT_Port, sensor->DHT_Pin)
#define lineUp()        GPIO_SetBits(sensor->DHT_Port, sensor->DHT_Pin)
#define getLine()       GPIO_ReadInputDataBit(sensor->DHT_Port, sensor->DHT_Pin)
#define Delay(d)        tos_sleep_ms(d)

static void goToOutput(DHT_sensor *sensor)
{
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
  lineUp();
}

static void goToInput(DHT_sensor *sensor)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  GPIO_InitStruct.GPIO_Pin = sensor->DHT_Pin;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(sensor->DHT_Port, &GPIO_InitStruct);
}

DHT_data DHT_getData(DHT_sensor *sensor)
{
    DHT_data data = {-128.0f, -128.0f};

    goToOutput(sensor);
    lineDown();
    Delay(18);
    lineUp();
    goToInput(sensor);

    uint16_t timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;

    while(!getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }
    timeout = 0;
    while(getLine())
    {
        timeout++;
        if (timeout > DHT_TIMEOUT)
        {
            return data;
        }
    }

    uint8_t rawData[5] = {0,0,0,0,0};
    for(uint8_t a = 0; a < 5; a++)
    {
        for(uint8_t b = 7; b != 255; b--)
        {
            uint16_t hT = 0, lT = 0;
            while(!getLine() && lT != 65535) lT++;
            timeout = 0;
            while(getLine()&& hT != 65535) hT++;
            if(hT > lT) rawData[a] |= (1<<b);
        }
    }

    if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4])
    {
        data.hum = (float)(((uint16_t)rawData[0]<<8) | rawData[1])*0.1f;
        if(!(rawData[2] & (1<<7)))
        {
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*0.1f;
        }
        else
        {
            rawData[2] &= ~(1<<7);
            data.temp = (float)(((uint16_t)rawData[2]<<8) | rawData[3])*-0.1f;
        }
    }

    return data;
}
